﻿// Copyright (c) Microsoft Open Technologies, Inc. All rights reserved. 
// See License.txt in the project root for license information.
using System.Threading.Actors;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Threading;
using System.Threading.Actors.Utilities;

namespace TestActorMethods
{
    // For general support methods.
    [TestClass]
    public class ActorUnitTests
    {
        internal static IActorState CreateActorState(ActorStateEnum stateType = ActorStateEnum.Standalone)
        {
            switch (stateType)
            {
                case ActorStateEnum.Standalone: return new StandaloneActorState();
                case ActorStateEnum.InMemory:
                    // Note: passing in a null IActorInteractor will cause a small number of methods to fail.  However,
                    // we don't have an implementation that doesn't depend on Fabric stuff, and our unit tests can't
                    // easily depend on System.Fabric due to bitness issues.
                    return new InMemoryActorState("bogusServiceName", null);
                default: throw new InvalidOperationException("Unknown actor state type " + stateType);
            }
        }

        public static void ExpectException<T>(Func<IActorState, Object[], Object> func, IActorState astate, params Object[] parameters) where T : Exception
        {
            bool returnedWithoutThrowing = false;
            try
            {
                func(astate, parameters);
                returnedWithoutThrowing = true;
            }
            catch (T expected)
            {
                // Good, but check type too.
                if (typeof(T) != expected.GetType())
                {
                    Assert.Fail("Exception threw a subclass of the exception we thought we should catch.  Probably not a problem, but check the spec.  Expected: {0}  Got: {1}", typeof(T), expected.GetType());
                }
            }
            catch (Exception e)
            {
                Assert.Fail("Function threw the wrong exception.  Expected: {0}  Got: {1}", typeof(T), e.GetType());
                throw e;
            }

            if (returnedWithoutThrowing)
            {
                Assert.Fail("ExpectException failed - function succeeded when we expected it to throw an exception.");
            }
        }

        public static void TestAcrossAllStateTypes(Action<ActorStateEnum> test)
        {
            foreach (ActorStateEnum stateType in Enum.GetValues(typeof(ActorStateEnum)))
                test(stateType);
        }
    }

    public enum ActorStateEnum
    {
        Standalone,
        InMemory
    }

    public static class Extensions
    {
        public static bool WaitForKey(this StandaloneActorState state, string keyName, int msTimeout)
        {
            CancellationTokenSource cts = new CancellationTokenSource();
            Task checker = Task.Factory.StartNew( ctObject =>
            {
                CancellationToken ct = (CancellationToken) ctObject;
                object unused;
                while(!state.TryGet(keyName, out unused))
                {
                    Thread.Sleep(10);
                    ct.ThrowIfCancellationRequested();
                }
            }, cts.Token, cts.Token);

            bool timedOut = !checker.Wait(msTimeout);

            if(timedOut) cts.Cancel();
            return !timedOut;
        }
    }
}
